import { NgClass } from '@angular/common';
import {
  ChangeDetectionStrategy,
  Component,
  computed,
  input,
  output,
  signal,
} from '@angular/core';
import {
  ControlValueAccessor,
  NG_VALUE_ACCESSOR,
} from '@angular/forms';
import { range } from '@core/utils';

@Component({
  selector: 'app-star-rating',
  standalone: true,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: StarRatingComponent,
      multi: true,
    },
  ],
  imports: [NgClass],
  templateUrl: './star-rating.component.html',
  styles: `
    :host {
      @apply flex flex-col;
    }
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class StarRatingComponent
  implements ControlValueAccessor
{
  // inputs
  scale = input(5);
  showRating = input(false, {
    transform: (val: string | boolean) =>
      val === 'true' || (typeof val === 'boolean' && val),
  });

  // outputs
  rated = output<number>();

  // internal states
  indices = computed(() => range(this.scale()));
  rating = signal(0);
  tentativeRating = signal(0);
  isDisabled = signal(false);
  isAnimating = signal(false);

  private _onChange?: Function;
  private _onTouch?: Function;

  // interaction
  rate(rating: number) {
    this.rating.set(rating);
    this.rated.emit(rating);

    this._onTouch && this._onTouch();
    this._onChange && this._onChange(rating);

    this.isAnimating.set(true);
  }

  // style calculation
  isCheckedAt(index: number) {
    if (this.isDisabled()) return false;

    return (
      index < this.tentativeRating() ||
      (this.rating() &&
        !this.tentativeRating() &&
        index < this.rating())
    );
  }

  isDimmedAt(index: number) {
    return this.isDisabled()
      ? index < this.rating()
      : this.tentativeRating() &&
          index >= this.tentativeRating() &&
          index < this.rating();
  }

  isUncheckedAt(index: number) {
    return this.isDisabled()
      ? index >= this.rating()
      : index >= this.tentativeRating() &&
          index >= this.rating();
  }

  // form integration
  writeValue(val: any): void {
    if (typeof val !== 'number') return;

    if (val > this.scale()) {
      val = Math.min(this.scale(), val);
      setTimeout(
        () => this._onChange && this._onChange(val),
      );
    } else {
      val = Math.max(0, val);
      setTimeout(
        () => this._onChange && this._onChange(val),
      );
    }

    this.rating.set(val);
  }
  registerOnChange(fn: (_: any) => void): void {
    this._onChange = fn;
  }
  registerOnTouched(fn: any): void {
    this._onTouch = fn;
  }
  setDisabledState?(isDisabled: boolean): void {
    this.isDisabled.set(isDisabled);
  }
}

export default StarRatingComponent;
